﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace Lxzhu.OpenGL.Core
{
    /// <summary>
    /// 要获取ReadOnlySet,必须先构造一个普通的Set 然后获取其ReadOnlySet属性.
    /// </summary>
    public class Set<T>:IList<T>
    {
        public Set()           
        {           
            this.mList = new List<T>();
        }
        public Set(int capacity)
        {
            this.mList = new List<T>(capacity);
        }
        public Set(IEnumerable<T> ptCol)
        {
            this.mList = new List<T>(ptCol);
        }
        private List<T> mList ;
        private bool mIsReadOnly = false;
        private Set<T> mReadOnlySet = null;
        /// <summary>
        /// 混洗这个集合.即使是只读的集合也可以被混洗
        /// </summary>
        public void Shuffle()
        {
            if (this.Count < 2) return;
            Random rand = new Random();
            double ratio = rand.NextDouble();
            if (ratio > 0.5) ratio = 0.5;
            int seed = (int)ratio * this.Count;
            int remain = this.Count - seed;
            for (int src = 0; src < seed; src++)
            {
                int dest = seed + rand.Next(remain);
                T t0 = mList[src];
                mList[src] = mList[dest];
                mList[dest] = t0;
            }
        }
        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return mList.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mList.GetEnumerator();
        }

        #endregion

        #region IList<T> Members

        public int IndexOf(T item)
        {
            return this.mList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            if (!IsReadOnly && item != null && !Contains(item))
            {
                mList.Insert(index, item);
                mReadOnlySet = null;
            }
        }

        public void RemoveAt(int index)
        {
            if (!IsReadOnly)
            {
                mList.RemoveAt(index);
                mReadOnlySet = null;
            }
        }

        public T this[int index]
        {
            get
            {
                return this.mList[index];
            }
            set
            {
                if (!IsReadOnly && value != null && !Contains(value))
                {
                    mList[index] = value;
                    mReadOnlySet = null;
                }
            }
        }

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            if (!IsReadOnly && item != null && !Contains(item))
            {
                mList.Add(item);
                mReadOnlySet = null;
            }
        }
        public void Clear()
        {
            if (!IsReadOnly)
            {
                mList.Clear();
                mReadOnlySet = null;
            }
        }

        public bool Contains(T item)
        {
            return this.mList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            mList.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return this.mList.Count; }
        }

        public bool IsReadOnly
        {
            get { return this.mIsReadOnly; }
        }

        public bool Remove(T item)
        {
            if (!IsReadOnly)
            {                
                bool ret= mList.Remove(item);
                if (ret) mReadOnlySet = null;
                return ret;
            }
            return false;
        }
        public Set<T> Union(IEnumerable<T> ptCol)
        {
            if (!IsReadOnly && ptCol != null)
            {
                bool changed = false;
                foreach (T item in ptCol)
                {
                    if (item != null && !Contains(item))
                    {
                        changed = true;
                        mList.Add(item);
                    }
                }
                if (changed)
                    mReadOnlySet = null;
            }
            return this;
        }
        public Set<T> Insersect(IEnumerable<T> ptCol)
        {
            if (!IsReadOnly && ptCol != null)
            {
                List<T> ret = new List<T>(ptCol);
                for (int i = this.Count - 1; i >= 0; i--)
                {
                    if (!ret.Contains(this[i]))
                        this.RemoveAt(i);
                }
            }
            return this;
        }
        public Set<T> Subtract(IEnumerable<T> ptCol)
        {
            if (!IsReadOnly && ptCol != null)
            {
                foreach (T item in ptCol)
                {
                    if (this.Contains(item))
                        this.Remove(item);
                }
            }
            return this;
        }
        public Set<T> ReadOnlySet
        {
            get
            {
                if (this.IsReadOnly) return this;
                else
                {
                    if (this.mReadOnlySet == null)
                    {
                        this.mReadOnlySet = new Set<T>(this);
                        this.mIsReadOnly = true;
                    }
                    return this.mReadOnlySet;
                }
            }
        }
        #endregion
        #region set functions
        public static Set<T> Union(IEnumerable<T> first, IEnumerable<T> second)
        {
            Set<T> ret = new Set<T>();
            return ret.Union(first).Union(second);           
        }
        public static Set<T> Insersect(IEnumerable<T> first, IEnumerable<T> second)
        {
            Set<T> ret = new Set<T>();
            return ret.Union(first).Insersect(second);
        }
        public static Set<T> Substract(IEnumerable<T> first, IEnumerable<T> second)
        {
            Set<T> ret = new Set<T>();
            return ret.Union(first).Subtract(second);
        }
        #endregion
    }
}
